package cn.edu.ctgu.ch9;

import com.github.tomakehurst.wiremock.WireMockServer;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.time.Duration;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
 * @author tiger54910
 * @create 2025/2/3 16:27
 */
public class DefaultsInHttpClient {
    private static WireMockServer wireMockServer;
    private static final int PORT = 9999;
    private static String HOST;

    @BeforeAll
    public static void setup() {
        wireMockServer = new WireMockServer(options().port(PORT));
        wireMockServer.start();
        HOST = String.format("http://localhost:%s", PORT);

        wireMockServer.stubFor(
                get(urlEqualTo("/data")).willReturn(aResponse().withStatus(200).withBody("some-data"))
        );

        wireMockServer.stubFor(
                get(urlEqualTo("/slow-data")).willReturn(aResponse().withStatus(200).withBody("some-data").withFixedDelay(5_000))
        );
    }

    @AfterAll
    public static void teardown() {
        if (wireMockServer != null) {
            wireMockServer.stop();
        }
    }

    @Test
    void shouldExecuteGetRequestsWithDefaults1() throws IOException {
        Request request = new Request.Builder().url(HOST + "/data").build();

        OkHttpClient client = new OkHttpClient.Builder().build();
        Call call = client.newCall(request);
        Response response = call.execute();

        assertThat(response.code()).isEqualTo(200);
        assertThat(response.body().string()).isEqualTo("some-data");
    }

    @Test
    void shouldExecuteGetRequestsToSlowEndpointWithDefaults() throws IOException {
        Request request = new Request.Builder().url(HOST + "/slow-data").build();

        OkHttpClient client = new OkHttpClient.Builder().build();
        Call call = client.newCall(request);

        long start = System.currentTimeMillis();
        Response response = call.execute();
        long totalTime = System.currentTimeMillis()-start;

        // the request took 5 seconds
        assertThat(totalTime).isGreaterThan(5_000);
        assertThat(response.code()).isEqualTo(200);
        assertThat(response.body().string()).isEqualTo("some-data");
    }

    @Test
    void shouldFailRequestAfterTimeout() {
        Request request = new Request.Builder().url(HOST + "/slow-data").build();

        OkHttpClient client = new OkHttpClient.Builder().readTimeout(Duration.ofMillis(100)).build();
        Call call = client.newCall(request);

        long start = System.currentTimeMillis();
        assertThatThrownBy(call::execute).isInstanceOf(SocketTimeoutException.class);
        long totalTime = System.currentTimeMillis() -start;

        // the request will fail fast
        assertThat(totalTime).isLessThan(5_000);
    }
}
